#include "StringUtils.h"
#include <limits.h>
#include <sstream>
#include <string.h>
#include <algorithm>

namespace StringUtils
{
  // StringUtils()
  // {
  // }

  /**
   * Example: subString("12345","1","4")=23
   *
   * @param src
   * @param start
   * @param to
   * @return
   */
  int subStringToInteger(string_ptr src, string_ptr start, string_ptr to)
  {
    return stringToInteger(subString(src, start, to));
  }

  /**
   * Example: subString("abcd","a","c")="b"
   *
   * @param src
   * @param start NULL while start from index=0
   * @param to    NULL while to index=src.length
   * @return
   */
  string_ptr subString(string_ptr src, string_ptr start, string_ptr to)
  {
    return subString(src, start, to, false);
  }

  /**
   * Example: subString("abcdc","a","c",true)="bcd"
   *
   * @param src
   * @param start  NULL while start from index=0
   * @param to     NULL while to index=src.length
   * @param toLast true while to index=src.lastIndexOf(to)
   * @return
   */
  string_ptr subString(string_ptr src, string_ptr start, string_ptr to, bool toLast)
  {
    int indexFrom = start->empty() ? 0 : src->find(*start);
    int indexTo;
    if (to->empty())
    {
      indexTo = src->length();
    }
    else
    {
      indexTo = toLast ? src->rfind(*to) : src->find(*to);
    }
    if (indexFrom < 0 || indexTo < 0 || indexFrom > indexTo)
    {
      return NULL;
    }

    if (!start->empty())
    {
      indexFrom += start->length();
    }

    return make_string_ptr(src->substr(indexFrom, indexTo));
  }

  /**
   * @param in
   * @return
   */
  int stringToInteger(string_ptr in)
  {
    if (in->empty())
    {
      return INT_MIN;
    }
    // in = in.trim();
    in = Trim(in);
    if (in->length() == 0)
    {
      return INT_MIN;
    }

    // try
    // {
    return atoi(in->c_str());
    // }
    // catch (NumberFormatException e)
    // {
    //   LOG.warn("stringToInteger fail,string=" + in, e);
    //   return NULL;
    // }
  }

  bool equals(string_ptr a, string_ptr b)
  {
    if (a->empty())
    {
      return b->empty();
    }
    return a->c_str() == b->c_str();
  }

  bool equalsIgnoreCase(string_ptr a, string_ptr b)
  {
    // if (a->empty())
    // {
    //   return b->empty();
    // }
    // // return a.equalsIgnoreCase(b);
    // return StringUtils::iequals(*a, *b);
    if (a->length() == b->length())
    {
      return std::equal(a->begin(), a->end(), b->begin(),
                        [](char a, char b)
                        {
                          return tolower(a) == tolower(b);
                        });
    }
    else
    {
      return false;
    }
  }

  bool iequals(string_ptr a, string_ptr b) { return equalsIgnoreCase(a, b); }
  bool iequals(std::wstring a, std::wstring b) { return equalsIgnoreCase(a, b); }
  bool iequals(std::string a, std::string b)
  {
    if (a.length() == b.length())
    {
      return std::equal(a.begin(), a.end(), b.begin(),
                        [](char a, char b)
                        {
                          return tolower(a) == tolower(b);
                        });
    }
    else
    {
      return false;
    }
  }

  bool equalsIgnoreCase(std::wstring a, std::wstring b)
  {
    // if (a.empty())
    // {
    //   return b.empty();
    // }
    // return StringUtils::iequals(a, b);

    if (a.length() == b.length())
    {
      return std::equal(a.begin(), a.end(), b.begin(),
                        [](char a, char b)
                        {
                          return tolower(a) == tolower(b);
                        });
    }
    else
    {
      return false;
    }
  }

  // bool isEmpty(CharSequence* value) {
  //     return value == NULL || value.length() == 0;
  // }

  int lowerHashCode(string_ptr text)
  {
    if (text->empty())
    {
      return 0;
    }
    //        return text.toLowerCase().hashCode();
    int h = 0;
    for (int i = 0; i < text->length(); ++i)
    {
      char ch = text->at(i);
      if (ch >= 'A' && ch <= 'Z')
      {
        ch = (char)(ch + 32);
      }

      h = 31 * h + ch;
    }
    return h;
  }

  bool isNumber(string_ptr str)
  {
    if (str->empty() || str->length() == 0)
    {
      return false;
    }
    int sz = str->length();
    bool hasExp = false;
    bool hasDecPoint = false;
    bool allowSigns = false;
    bool foundDigit = false;
    // deal with any possible sign up front
    int start = (str->at(0) == '-') ? 1 : 0;
    if (sz > start + 1)
    {
      if (str->at(start) == '0' && str->at(start + 1) == 'x')
      {
        int i = start + 2;
        if (i == sz)
        {
          return false; // str == "0x"
        }
        // checking hex (it can't be anything else)
        for (; i < str->length(); i++)
        {
          char ch = str->at(i);
          if ((ch < '0' || ch > '9') && (ch < 'a' || ch > 'f') && (ch < 'A' || ch > 'F'))
          {
            return false;
          }
        }
        return true;
      }
    }
    sz--; // don't want to loop to the last char, check it afterwords
    // for type qualifiers
    int i = start;
    // loop to the next to last char or to the last char if we need another digit to
    // make a valid number (e.g. chars[0..5] = "1234E")
    while (i < sz || (i < sz + 1 && allowSigns && !foundDigit))
    {
      char ch = str->at(i);
      if (ch >= '0' && ch <= '9')
      {
        foundDigit = true;
        allowSigns = false;
      }
      else if (ch == '.')
      {
        if (hasDecPoint || hasExp)
        {
          // two decimal points or dec in exponent
          return false;
        }
        hasDecPoint = true;
      }
      else if (ch == 'e' || ch == 'E')
      {
        // we've already taken care of hex.
        if (hasExp)
        {
          // two E's
          return false;
        }
        if (!foundDigit)
        {
          return false;
        }
        hasExp = true;
        allowSigns = true;
      }
      else if (ch == '+' || ch == '-')
      {
        if (!allowSigns)
        {
          return false;
        }
        allowSigns = false;
        foundDigit = false; // we need a digit after the E
      }
      else
      {
        return false;
      }
      i++;
    }
    if (i < str->length())
    {
      char ch = str->at(i);

      if (ch >= '0' && ch <= '9')
      {
        // no type qualifier, OK
        return true;
      }
      if (ch == 'e' || ch == 'E')
      {
        // can't have an E at the last byte
        return false;
      }
      if (!allowSigns && (ch == 'd' || ch == 'D' || ch == 'f' || ch == 'F'))
      {
        return foundDigit;
      }
      if (ch == 'l' || ch == 'L')
      {
        // not allowing L with an exponent
        return foundDigit && !hasExp;
      }
      // last character is illegal
      return false;
    }
    // allowSigns is true iff the val ends in 'E'
    // found digit it to make sure weird stuff like '.' and '1E-' doesn't pass
    return !allowSigns && foundDigit;
  }

  bool isNumber(char *chars)
  {
    if (chars == NULL || strlen(chars) == 0)
    {
      return false;
    }
    int sz = strlen(chars);
    bool hasExp = false;
    bool hasDecPoint = false;
    bool allowSigns = false;
    bool foundDigit = false;
    // deal with any possible sign up front
    int start = (chars[0] == '-') ? 1 : 0;
    if (sz > start + 1)
    {
      if (chars[start] == '0' && chars[start + 1] == 'x')
      {
        int i = start + 2;
        if (i == sz)
        {
          return false; // str == "0x"
        }
        // checking hex (it can't be anything else)
        for (; i < strlen(chars); i++)
        {
          char ch = chars[i];
          if ((ch < '0' || ch > '9') && (ch < 'a' || ch > 'f') && (ch < 'A' || ch > 'F'))
          {
            return false;
          }
        }
        return true;
      }
    }
    sz--; // don't want to loop to the last char, check it afterwords
    // for type qualifiers
    int i = start;
    // loop to the next to last char or to the last char if we need another digit to
    // make a valid number (e.g. chars[0..5] = "1234E")
    while (i < sz || (i < sz + 1 && allowSigns && !foundDigit))
    {
      char ch = chars[i];
      if (ch >= '0' && ch <= '9')
      {
        foundDigit = true;
        allowSigns = false;
      }
      else if (ch == '.')
      {
        if (hasDecPoint || hasExp)
        {
          // two decimal points or dec in exponent
          return false;
        }
        hasDecPoint = true;
      }
      else if (ch == 'e' || ch == 'E')
      {
        // we've already taken care of hex.
        if (hasExp)
        {
          // two E's
          return false;
        }
        if (!foundDigit)
        {
          return false;
        }
        hasExp = true;
        allowSigns = true;
      }
      else if (ch == '+' || ch == '-')
      {
        if (!allowSigns)
        {
          return false;
        }
        allowSigns = false;
        foundDigit = false; // we need a digit after the E
      }
      else
      {
        return false;
      }
      i++;
    }
    if (i < strlen(chars))
    {
      char ch = chars[i];
      if (ch >= '0' && ch <= '9')
      {
        // no type qualifier, OK
        return true;
      }
      if (ch == 'e' || ch == 'E')
      {
        // can't have an E at the last byte
        return false;
      }
      if (!allowSigns && (ch == 'd' || ch == 'D' || ch == 'f' || ch == 'F'))
      {
        return foundDigit;
      }
      if (ch == 'l' || ch == 'L')
      {
        // not allowing L with an exponent
        return foundDigit && !hasExp;
      }

      if (ch == '.')
      {
        return true;
      }
      // last character is illegal
      return false;
    }
    // allowSigns is true iff the val ends in 'E'
    // found digit it to make sure weird stuff like '.' and '1E-' doesn't pass
    return !allowSigns && foundDigit;
  }

  string_ptr formatDateTime19(long millis, time_t timeZone)
  {
    tm *p = gmtime(&timeZone);

    char chars[19];
    sprintf(chars, "%d-%d-%d %d:%d:%d", 1900 + p->tm_year, 1 + p->tm_mon, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);
    return make_string_ptr(chars);
    // printf("%s %d:%d:%d\n", wday[p->tm_wday], p->tm_hour,
    //     p->tm_min, p->tm_sec);
    // Calendar cale = timeZone == NULL
    //         ? Calendar.getInstance()
    //         : Calendar.getInstance(timeZone);
    // cale.setTimeInMillis(millis);

    // int year = cale.get(Calendar.YEAR);
    // int month = cale.get(Calendar.MONTH) + 1;
    // int dayOfMonth = cale.get(Calendar.DAY_OF_MONTH);
    // int hour = cale.get(Calendar.HOUR_OF_DAY);
    // int minute = cale.get(Calendar.MINUTE);
    // int second = cale.get(Calendar.SECOND);

    // char chars[19];
    // chars[0] = (char) (year / 1000 + '0');
    // chars[1] = (char) ((year / 100) % 10 + '0');
    // chars[2] = (char) ((year / 10) % 10 + '0');
    // chars[3] = (char) (year % 10 + '0');
    // chars[4] = '-';
    // chars[5] = (char) (month / 10 + '0');
    // chars[6] = (char) (month % 10 + '0');
    // chars[7] = '-';
    // chars[8] = (char) (dayOfMonth / 10 + '0');
    // chars[9] = (char) (dayOfMonth % 10 + '0');
    // chars[10] = ' ';
    // chars[11] = (char) (hour / 10 + '0');
    // chars[12] = (char) (hour % 10 + '0');
    // chars[13] = ':';
    // chars[14] = (char) (minute / 10 + '0');
    // chars[15] = (char) (minute % 10 + '0');
    // chars[16] = ':';
    // chars[17] = (char) (second / 10 + '0');
    // chars[18] = (char) (second % 10 + '0');
    // return std::to_string(chars);
  }

  // string_ptr removeNameQuotes(string_ptr s)
  // {
  //   if (s->empty() || s->length() <= 1)
  //   {
  //     return NULL;
  //   }
  //   int len = s->length();
  //   char c0 = s->at(0);
  //   char last = s->at(len - 1);

  //   if (c0 == last && (c0 == '`' || c0 == '\'' || c0 == '\"'))
  //   {
  //     return make_string_ptr(s->substr(1, len - 1));
  //   }
  //   return s;
  // }

  string_ptr removeNameQuotes(string_ptr s)
  {
    if (s->empty() || s->length() <= 1)
    {
      return NULL;
    }
    int len = s->length();
    char c0 = s->at(0);
    char last = s->at(len - 1);

    if (c0 == last && (c0 == '`' || c0 == '\'' || c0 == '\"'))
    {
      return make_string_ptr(s->substr(1, len - 1));
    }
    return s;
  }

  bool endWith(const string_ptr str, const string_ptr tail)
  {
    return str->compare(str->size() - tail->size(), tail->size(), *tail) == 0;
  }

  bool startWith(const string_ptr str, const string_ptr head, std::size_t pos)
  {
    return str->compare(pos, head->size(), *head) == 0;
  }

  void split(const string_ptr strtem, const char a, std::vector<string_ptr> &res)
  {
    res.clear();
    size_t pos1, pos2;
    pos2 = strtem->find(a);
    pos1 = 0;
    while (std::string::npos != pos2)
    {
      res.push_back(make_string_ptr(strtem->substr(pos1, pos2 - pos1)));
      pos1 = pos2 + 1;
      pos2 = strtem->find(a, pos1);
    }
    res.push_back(make_string_ptr(strtem->substr(pos1)));
  }

  bool isEmpty(string_ptr value)
  {
    return value->empty();
  }

  // bool isEmpty(string_ptr  value) {
  //   return value.empty();
  // }

  string_ptr toUpper(string_ptr str)
  {
    std::string out;
    for (int i = 0; i < str->length(); i++)
    {
      out[i] = std::toupper(str->at(i));
    }
    return make_string_ptr(out);
  }

  string_ptr toLower(string_ptr str)
  {
    if (str == nullptr)
    {
      return nullptr;
    }

    std::string out;
    // for (int i = 0; i < str->length(); i++)
    // {
    //   out[i] = std::tolower(str->at(i));
    // }
    // out[str->length()] = 0;

    // printf("%s\n", out.c_str());
    std::transform(str->begin(), str->end(), std::back_inserter(out), [](unsigned char c)
                   { return std::tolower(c); });
    return make_string_ptr(out.c_str());
  }

  std::wstring toLower(std::wstring str)
  {
    std::wstring out;

    std::transform(str.begin(), str.end(), std::back_inserter(out), [](unsigned char c)
                   { return std::tolower(c); });
    return out;
  }

  string_ptr replaceAll(const string_ptr str, const string_ptr oldStr, const string_ptr newStr)
  {
    std::string tmp_str = str->c_str();

    size_t pos = tmp_str.find(*oldStr);
    while (pos != std::string::npos)
    {
      tmp_str.replace(pos, oldStr->length(), *newStr);
      pos = tmp_str.find(*oldStr, pos + newStr->length());
    }

    return make_string_ptr(tmp_str);
  }

  string_ptr Trim(const string_ptr str)
  {
    std::string tmp_str = str->c_str();

    tmp_str.erase(tmp_str.find_first_of(" "), tmp_str.find_last_of(" ") - tmp_str.find_first_of(" ") + 1);

    return make_string_ptr(tmp_str);
  }

  std::string charToString(char c)
  {
    std::stringstream ss;
    ss << c;
    return ss.str();
  }

  // trim from start
  std::string ltrim(std::string &s)
  {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
    return s;
  }

  // trim from end
  std::string rtrim(std::string &s)
  {
    s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
    return s;
  }

  // trim from both ends
  std::string trim(std::string &s)
  {
    s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
    return s;
  }

  // trim from start
  std::wstring ltrim(std::wstring &s)
  {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
    return s;
  }

  // trim from end
  std::wstring rtrim(std::wstring &s)
  {
    s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
    return s;
  }

  // trim from both ends
  std::wstring trim(std::wstring &s)
  {
    s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
    return s;
  }

  std::string deleteComment(std::string str)
  {
    if (str.empty())
      // 如果 str 为空
      return str;
    if (str.find("//") == str.npos &&
        (str.find("/*") == str.npos || str.find("*/") == str.npos))
      // 如果没有 // 注释 且没有/**/注释
      return str;
    int len = str.size();

    for (int i = 0; i < len; ++i)
    {
      // 如果先遇到 // 注释
      if (str[i] == '/' && i + 1 < len && str[i + 1] == '/')
      {
        int j = i;
        while (j < len && str[j] != '\n')
        {
          str[j] = ' ';
          j++;
        }
        i = j;
      }
      // 如果遇到 /*注释
      else if (str[i] == '/' && i + 1 < len && str[i + 1] == '*')
      {
        str[i] = ' ';
        str[i + 1] = ' ';
        int j = i + 2;
        while (j < len && j + 1 < len && !(str[j] == '*' && str[j + 1] == '/'))
        {
          str[j] = ' ';
          j++;
        }
        if (j < len && j + 1 < len)
        {
          str[j] = ' ';
          str[j + 1] = ' ';
        }
        i = j + 1;
      }
      // 如果遇到 ""
      else if (str[i] == '"')
      {
        int j = i + 1;
        while (j < len && str[j] != '"')
        {
          j++;
        }
        i = j;
      }
    }
    return str;
  }

}